<template>
  <a-input-group compact>
    <a-select v-model="selfyear" style="width:100px">
      <a-select-option v-for="item in selfyears" :key="item" :value="item">
        {{ item }}
      </a-select-option>
    </a-select>
    <a-select v-model="selfmonth" style="width:60px">
      <a-select-option v-for="item in months" :key="item" :value="item">
        {{ item }}
      </a-select-option>
    </a-select>
    <a-select v-model="selfweek" style="width:60px">
      <a-select-option v-for="item in weeks" :key="item" :value="item">
        {{ item }}
      </a-select-option>
    </a-select>
  </a-input-group>
</template>

<script>
export default {
  props: {
    //定义按周几作为一周的开始
    beginDay: {
      type: Number,
      default: 6,
    }, //周开始为周六，周日为 0，以此类推
    //年份范围，默认从当年往前取20年
    years: {
      type: Array,
      default() {
        return Array.from(new Array(20).keys()).map(
          (v, i) => new Date().getFullYear() - i
        );
      },
    },
    //选中年份
    year: {
      type: Number,
      default: new Date().getFullYear(),
    },
    //选中月份
    month: {
      type: Number,
      default: new Date().getMonth() + 1,
    },
    //选中周数
    week: {
      type: Number,
      default: 1,
    },
    //日期范围开始
    availableFromDate: {
      type: Date,
      default: null,
    },
    //日期范围结束
    availableToDate: {
      type: Date,
      default: null,
    },
  },
  watch: {
    years() {
      this.selfyears = this.years;
    },
    year() {
      this.selfyear = this.year;
    },
    month() {
      this.selfmonth = this.month;
    },
    week() {
      this.selfweek = this.week;
    },
    selfyear() {
      this.getAvailableMonths();
      if (this.months.indexOf(this.selfmonth) == -1)
        this.selfmonth = this.months[0];
      this.getAvailableWeeks();
      if (this.weeks.indexOf(this.selfweek) == -1)
        this.selfweek = this.weeks[0];
      this.changeHandler();
    },
    selfmonth() {
      this.getAvailableWeeks();
      if (this.weeks.indexOf(this.selfweek) == -1)
        this.selfweek = this.weeks[0];
      this.changeHandler();
    },
    selfweek() {
      this.changeHandler();
      this.availableDateChange();
    },
    availableFromDate() {
      this.availableDateChange();
    },
    availableToDate() {
      this.availableDateChange();
    },
  },
  data() {
    return {
      selfyears: [],
      months: Array.from(new Array(12).keys()).map((i) => i + 1),
      weeks: [1, 2, 3, 4, 5],
      selfyear: new Date().getFullYear(),
      selfmonth: new Date().getMonth() + 1,
      selfweek: 1,
      fromDate: "",
      toDate: "",
      yearMonthWeeks: {}, //年月的周缓存,
      yearMonthWeekDates: {}, //年月周的日期范围缓存
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.selfyears = this.years;
      this.getAvailableYears();

      if (this.selfyears.indexOf(this.year) > -1) this.selfyear = this.year;
      else this.selfyear = this.selfyears[0];

      this.getAvailableMonths();
      if (this.months.indexOf(this.month) > -1) this.selfmonth = this.month;
      else this.selfmonth = this.months[0];

      this.getAvailableWeeks();
      if (this.weeks.indexOf(this.week) > -1) this.selfweek = this.week;
      else this.selfweek = this.weeks[0];

      this.getWeeks();
    },
    availableDateChange() {
      this.getAvailableYears();

      if (this.selfyears.indexOf(this.selfyear) == -1)
        this.selfyear = this.selfyears[0];

      this.getAvailableMonths();
      if (this.months.indexOf(this.selfmonth) == -1)
        this.selfmonth = this.months[0];

      this.getAvailableWeeks();
      if (this.weeks.indexOf(this.selfweek) == -1)
        this.selfweek = this.weeks[0];
    },
    getWeeks(date) {
      //计算周的长度
      if (!(date instanceof Date))
        date = new Date(this.selfyear, this.selfmonth - 1, 1);
      //   console.log(selfweek);
      var selfyear = date.getFullYear();
      var selfmonth = date.getMonth() + 1;
      //如果缓存没有，则进行计算
      if (!this.yearMonthWeeks[selfyear + "-" + selfmonth]) {
        this.calculateWeeks(date);
      }
      this.weeks = this.yearMonthWeeks[selfyear + "-" + selfmonth];
    },
    calculateWeeks(date) {
      date.setDate(1);
      var selfyear = date.getFullYear();
      var selfmonth = date.getMonth() + 1;
      //   console.log(date);
      var selfweek = date.getDay(); //周日 是 0
      //
      //计算临界是周几
      var borderlineWeek = this.beginDay + 3;
      if (borderlineWeek >= 7) borderlineWeek = borderlineWeek - 7;

      //   console.log(borderlineWeek);
      //根据起始周几与临界周几计算月初月末落点范围（在范围内才算是这个月的周）
      //遍历0-3 （4天）,如果加起来的周数大于等于7 则通过减掉7（周日是从0 开始，所以需要这么算）
      var startRange = Array.from(new Array(4).keys()).map((i) =>
        this.beginDay + i >= 7 ? this.beginDay + i - 7 : this.beginDay + i
      );
      //   console.log(startRange);
      var endRange = Array.from(new Array(4).keys()).map((i) =>
        borderlineWeek + i >= 7 ? borderlineWeek + i - 7 : borderlineWeek + i
      );

      //   console.log(endRange);

      //判断这个月的1 号是否 落在 起始范围
      if (startRange.indexOf(selfweek) > -1) {
        //月初在第一周，
        date.setDate(date.getDate() - startRange.indexOf(selfweek));
      } else {
        //月初不在第一周，
        date.setDate(date.getDate() + (4 - endRange.indexOf(selfweek)));
      }
      //   console.log("初始日期", date.getMonth() + 1, date.getDate());

      var weekNum = 1; //第几周
      var weeksOfYearMonth = [];
      date.setDate(date.getDate() - 1);
      while (true) {
        var datesOfYearMonthWeek = [];

        for (let i = 0; i < 7; i++) {
          date.setDate(date.getDate() + 1);
          datesOfYearMonthWeek.push(new Date(date.valueOf()));
        }

        //判断累加后的日期是否还是当前月，是 的话，则直接缓存到数组
        if (date.getMonth() + 1 == selfmonth) {
          weeksOfYearMonth.push(weekNum);
          this.yearMonthWeekDates[
            selfyear + "-" + selfmonth + "-" + weekNum
          ] = datesOfYearMonthWeek;
          weekNum++;
        } else {
          //不是的话，判断月末是否在月末的周几范围内
          var endDate = new Date(date.valueOf());
          endDate.setDate(0);
          //console.log("月末日期", endDate);
          if (endRange.indexOf(endDate.getDay()) > -1) {
            weeksOfYearMonth.push(weekNum);
            this.yearMonthWeekDates[
              selfyear + "-" + selfmonth + "-" + weekNum
            ] = datesOfYearMonthWeek;
          }
          break;
        }
      }
      this.yearMonthWeeks[selfyear + "-" + selfmonth] = weeksOfYearMonth;

      //   console.log(this.yearMonthWeeks);
      //   console.log(this.yearMonthWeekDates);
    },
    getCurrentValue() {
      var datesOfYearMonthWeek = this.yearMonthWeekDates[
        this.selfyear + "-" + this.selfmonth + "-" + this.selfweek
      ];
      if (!datesOfYearMonthWeek) return null;
      return {
        year: this.selfyear,
        month: this.selfmonth,
        week: this.selfweek,
        fromDate: datesOfYearMonthWeek[0],
        toDate: datesOfYearMonthWeek[6],
        dates: datesOfYearMonthWeek.map((d) => new Date(d.valueOf())),
      };
    },
    changeHandler() {
      console.log(this.getCurrentValue());
      this.$emit("change", this.getCurrentValue());
    },
    //指定年月周（对外提供调用，也可以直接通过 prop 传入 年月周）
    setValue(year, month, week) {
      this.selfyear = year;
      this.selfmonth = month;
      var date = new Date(year, month - 1, 1);
      this.getWeeks(date);
      this.selfweek = week || this.weeks[0];
    },
    /**筛选有效年份 */
    getAvailableYears() {
      var years = [];

      for (let i = 0; i < this.years.length; i++) {
        var year = this.years[i];
        if (
          this.availableFromDate &&
          this.availableFromDate.getFullYear() > year
        )
          continue;
        if (this.availableToDate && this.availableToDate.getFullYear() < year)
          continue;

        var months = this.getAvailableMonthsOfYear(year);
        if (months && months.length > 0) years.push(year);
      }
      this.selfyears = years;
    },
    /**筛选有效月份 */
    getAvailableMonths() {
      this.months = this.getAvailableMonthsOfYear(this.selfyear);
    },
    getAvailableMonthsOfYear(selfyear) {
      var fromDate = null;
      if (this.availableFromDate) {
        fromDate = new Date(
          this.availableFromDate.getFullYear(),
          this.availableFromDate.getMonth(),
          1
        );
      }
      var toDate = null;
      if (this.availableToDate) {
        toDate = new Date(
          this.availableToDate.getFullYear(),
          this.availableToDate.getMonth(),
          1
        );
      }

      var months = [];
      var oldMonths = Array.from(new Array(12).keys()).map((i) => i + 1);
      for (let i = 0; i < 12; i++) {
        var month = oldMonths[i];
        if (!selfyear) continue;
        var tempDate = new Date(selfyear, month - 1, 1);
        if (fromDate && fromDate.getTime() > tempDate.getTime()) continue;
        if (toDate && toDate.getTime() < tempDate.getTime()) continue;
        var weeks = this.getAvailableWeeksOfMonth(selfyear, month);
        if (weeks && weeks.length > 0) months.push(month);
      }
      return months;
    },
    /**获取可选的周 */
    getAvailableWeeks() {
      this.weeks = this.getAvailableWeeksOfMonth(this.selfyear, this.selfmonth);
    },
    getAvailableWeeksOfMonth(selfyear, selfmonth) {
      var fromDate = null;
      if (this.availableFromDate) {
        fromDate = new Date(
          this.availableFromDate.getFullYear(),
          this.availableFromDate.getMonth(),
          this.availableFromDate.getDate()
        );
      }
      var toDate = null;
      if (this.availableToDate) {
        toDate = new Date(
          this.availableToDate.getFullYear(),
          this.availableToDate.getMonth(),
          this.availableToDate.getDate()
        );
      }
      var weeks = [];

      this.getWeeks(new Date(selfyear, selfmonth - 1, 1));

      var allWeeks = this.yearMonthWeeks[selfyear + "-" + selfmonth];

      for (let i = 0; i < allWeeks.length; i++) {
        var week = allWeeks[i];
        var datesOfWeek = this.yearMonthWeekDates[
          selfyear + "-" + selfmonth + "-" + week
        ];
        //起始时间大于一周的开始，那么那一周将无法 呈现
        if (fromDate && fromDate.getTime() > datesOfWeek[0].getTime()) continue;
        //结束时间小于一周的结束，那么那一周将无法 呈现
        if (toDate && toDate.getTime() < datesOfWeek[6].getTime()) continue;

        weeks.push(week);
      }

      return weeks;
    },
  },
};
</script>

<style></style>
